package com.chan.mediacamera.clip;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Build;
import android.support.annotation.RequiresApi;

import com.chan.mediacamera.camera.media.MuxerWapper;

import java.io.IOException;
import java.nio.ByteBuffer;

import static android.media.MediaExtractor.SEEK_TO_PREVIOUS_SYNC;

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
public class AudioClipper implements Runnable {
    private MediaExtractor mAudioExtractor;
    private MediaCodec mAudioEncoder;
    private MediaCodec mAudioDecoder;
    private long mFirstVideoTime;

    private MediaClipper.ClipperConfig mConfig;
    private long mStartPosition;
    private long mClipDur;
    private OnClipperListener mlistener;
    private int muxAudioTrack = -1;

    public AudioClipper(OnClipperListener listener) {
        mlistener = listener;
    }


    public void release() {
        if (mAudioExtractor != null) {
            mAudioExtractor.release();
            mAudioExtractor = null;
        }
        if (mAudioEncoder != null) {
            mAudioEncoder.stop();
            mAudioEncoder.release();
            mAudioEncoder = null;
        }
        if (mAudioDecoder != null) {
            mAudioDecoder.stop();
            mAudioDecoder.release();
            mAudioDecoder = null;
        }
    }

    public void startClip(MediaClipper.ClipperConfig config, long startPosition, long dur) {
        mConfig = config;
        mStartPosition = startPosition;
        mClipDur = dur;
        prepare();
    }

    @Override
    public void run() {
        start();
        clipping();
        end();
    }

    protected void prepare() {
        try {
            mAudioDecoder = MediaCodec.createDecoderByType("audio/mp4a-latm");
            mAudioEncoder = MediaCodec.createEncoderByType("audio/mp4a-latm");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    protected void start() {
        try {
            mAudioExtractor = new MediaExtractor();
            mAudioExtractor.setDataSource(mConfig.mPath);
            int mAudioTrackIndex = -1;
            MediaFormat mAudioFormat = null;
            //音轨和视轨初始化
            for (int i = 0; i < mAudioExtractor.getTrackCount(); i++) {
                MediaFormat format = mAudioExtractor.getTrackFormat(i);
                if (format.getString(MediaFormat.KEY_MIME).startsWith("audio/")) {
                    mAudioTrackIndex = i;
                    mAudioFormat = format;
                    break;
                }
            }

            assert mAudioTrackIndex != -1;
            mAudioExtractor.selectTrack(mAudioTrackIndex);
            mFirstVideoTime = mAudioExtractor.getSampleTime();
            mAudioExtractor.seekTo(mFirstVideoTime + mStartPosition, SEEK_TO_PREVIOUS_SYNC);

            // Decoder
            mAudioDecoder.configure(mAudioFormat, null, null, 0);
            mAudioDecoder.start();

            // Encoder
            int sampleRate = mAudioFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);// 采样率
            int channelCount = mAudioFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
            int mbitrate = mAudioFormat.getInteger(MediaFormat.KEY_BIT_RATE);
            MediaFormat format = MediaFormat.createAudioFormat("audio/mp4a-latm", sampleRate, channelCount);//这里一定要注意声道的问题
            format.setInteger(MediaFormat.KEY_BIT_RATE, mbitrate);//比特率
            format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
            mAudioEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            mAudioEncoder.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void end() {
        if (mlistener != null) {
            mlistener.onStop(MuxerWapper.DATA_AUDIO);
        }
    }


    protected void clipping() {
        startAudioCodec(mAudioDecoder, mAudioEncoder, mAudioExtractor, mFirstVideoTime, mStartPosition, mClipDur);
    }


    private void startAudioCodec(MediaCodec decoder, MediaCodec encoder, MediaExtractor extractor, long firstSampleTime, long startPosition, long duration) {
        ByteBuffer[] decoderInputBuffers = decoder.getInputBuffers();
        ByteBuffer[] decoderOutputBuffers = decoder.getOutputBuffers();
        ByteBuffer[] encoderInputBuffers = encoder.getInputBuffers();
        ByteBuffer[] encoderOutputBuffers = encoder.getOutputBuffers();
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        MediaCodec.BufferInfo outputInfo = new MediaCodec.BufferInfo();
        boolean done = false;//用于判断整个编解码过程是否结束
        boolean inputDone = false;
        boolean decodeDone = false;
        extractor.seekTo(firstSampleTime + startPosition, SEEK_TO_PREVIOUS_SYNC);
        int decodeinput = 0;
        int encodeinput = 0;
        int encodeoutput = 0;
        long lastEncodeOutputTimeStamp = -1;
        while (!done) {
            if (!inputDone) {
                int inputIndex = decoder.dequeueInputBuffer(0);
                if (inputIndex >= 0) {
                    ByteBuffer inputBuffer;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        inputBuffer = decoder.getInputBuffer(inputIndex);
                    } else {
                        inputBuffer = decoderInputBuffers[inputIndex];
                    }
                    assert inputBuffer != null;
                    inputBuffer.clear();
                    int readSampleData = extractor.readSampleData(inputBuffer, 0);
                    long dur = extractor.getSampleTime() - firstSampleTime - startPosition;
                    if ((dur < duration) && readSampleData > 0) {
                        decoder.queueInputBuffer(inputIndex, 0, readSampleData, extractor.getSampleTime(), 0);
                        decodeinput++;
                        //Log.d(TAG, "videoCliper audio decodeinput" + decodeinput + " dataSize" + readSampleData + " sampeTime" + extractor.getSampleTime());
                        extractor.advance();
                    } else {
                        decoder.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        //Log.d(TAG, "videoCliper audio decodeInput end");
                        inputDone = true;
                    }
                }
            }
            if (!decodeDone) {
                int index = decoder.dequeueOutputBuffer(info, 0);
                if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
                    // no output available yet
                } else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                    decoderOutputBuffers = decoder.getOutputBuffers();
                } else if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                    // expected before first buffer of data
                    MediaFormat newFormat = decoder.getOutputFormat();
                } else if (index < 0) {
                } else {
                    boolean canEncode = (info.size != 0 && info.presentationTimeUs - firstSampleTime > startPosition);
                    boolean endOfStream = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
                    if (canEncode && !endOfStream) {
                        ByteBuffer decoderOutputBuffer;
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                            decoderOutputBuffer = decoder.getOutputBuffer(index);
                        } else {
                            decoderOutputBuffer = decoderOutputBuffers[index];
                        }
                        int encodeInputIndex = encoder.dequeueInputBuffer(0);
                        if (encodeInputIndex >= 0) {
                            ByteBuffer encoderInputBuffer = encoderInputBuffers[encodeInputIndex];
                            encoderInputBuffer.clear();
                            if (info.size < 4096) {//这里看起来应该是16位单声道转16位双声道
                                byte[] chunkPCM = new byte[info.size];
                                decoderOutputBuffer.get(chunkPCM);
                                decoderOutputBuffer.clear();
                                //说明是单声道的,需要转换一下
                                byte[] stereoBytes = new byte[info.size * 2];
                                for (int i = 0; i < info.size; i += 2) {
                                    stereoBytes[i * 2 + 0] = chunkPCM[i];
                                    stereoBytes[i * 2 + 1] = chunkPCM[i + 1];
                                    stereoBytes[i * 2 + 2] = chunkPCM[i];
                                    stereoBytes[i * 2 + 3] = chunkPCM[i + 1];
                                }
                                encoderInputBuffer.put(stereoBytes);
                                encoder.queueInputBuffer(encodeInputIndex, 0, stereoBytes.length, info.presentationTimeUs, 0);
                                encodeinput++;
                                //Log.d(TAG, "videoCliper audio encodeInput" + encodeinput + " dataSize" + info.size + " sampeTime" + info.presentationTimeUs);
                            } else {
                                encoderInputBuffer.put(decoderOutputBuffer);
                                encoder.queueInputBuffer(encodeInputIndex, info.offset, info.size, info.presentationTimeUs, 0);
                                encodeinput++;
                                //Log.d(TAG, "videoCliper audio encodeInput" + encodeinput + " dataSize" + info.size + " sampeTime" + info.presentationTimeUs);
                            }
                        }
                    }
                    if (endOfStream) {
                        int encodeInputIndex = encoder.dequeueInputBuffer(0);
                        encoder.queueInputBuffer(encodeInputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        //Log.d(TAG, "videoCliper audio encodeInput end");
                        decodeDone = true;
                    }
                    decoder.releaseOutputBuffer(index, false);
                }
            }
            boolean encoderOutputAvailable = true;
            while (encoderOutputAvailable) {
                int encoderStatus = encoder.dequeueOutputBuffer(outputInfo, 0);
                if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
                    // no output available yet
                    encoderOutputAvailable = false;
                } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                    encoderOutputBuffers = encoder.getOutputBuffers();
                } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                    MediaFormat newFormat = encoder.getOutputFormat();
                    if (mlistener != null) {
                        muxAudioTrack = mlistener.onFormatChanged(newFormat);
                        mlistener.onStart(MuxerWapper.DATA_AUDIO);
                    }
                } else if (encoderStatus < 0) {
                } else {
                    ByteBuffer encodedData;
                    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
                        encodedData = encoder.getOutputBuffer(encoderStatus);
                    } else {
                        encodedData = encoderOutputBuffers[encoderStatus];
                    }
                    done = (outputInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
                    if (done) {
                        encoderOutputAvailable = false;
                    }
                    // Write the data to the output "file".
                    if (outputInfo.presentationTimeUs == 0 && !done) {
                        continue;
                    }
                    if (outputInfo.size != 0 && outputInfo.presentationTimeUs > 0) {
                        if (outputInfo.presentationTimeUs > lastEncodeOutputTimeStamp) {//为了避免有问题的数据
                            encodeoutput++;
                            //Log.d(TAG, "videoCliper audio encodeOutput" + encodeoutput + " dataSize" + outputInfo.size + " sampeTime" + outputInfo.presentationTimeUs);
                            mlistener.writeData(muxAudioTrack, encodedData, outputInfo);
                            lastEncodeOutputTimeStamp = outputInfo.presentationTimeUs;
                        }
                    }

                    encoder.releaseOutputBuffer(encoderStatus, false);
                }
                if (encoderStatus != MediaCodec.INFO_TRY_AGAIN_LATER) {
                    // Continue attempts to drain output.
                    continue;
                }
            }
        }
    }
}
